#ifndef M_FMT_H__
#define M_FMT_H__
#include "level.hpp"
#include "message.hpp"
#include <vector>
#include <cassert>
#include <sstream>
namespace xglog
{
  // 抽象化格式化子项基类
  class FormatItem
  {
  public:
    using ptr = std::shared_ptr<FormatItem>;
    virtual void format(std::ostream &out, const LogMsg &msg) = 0; // 纯虚函数
  };
  // 派生格式化子项子类---消息，等级，时间，文件名，行号，线程id，日志器名称，制表符，换行，其他
  class MsgFormatItem : public FormatItem
  {
  public:
    void format(std::ostream &out, const LogMsg &msg) override
    {
      out << msg._payload;
    }
  };
  class LevFormatItem : public FormatItem
  {
  public:
    void format(std::ostream &out, const LogMsg &msg) override
    {
      out << LogLevel::toString(msg._level);
    }
  };
  class TimeFormatItem : public FormatItem
  {
    // 把时间戳转成能认识的时间
    // 根据转化格式生成不同的时间显示
  public:
    TimeFormatItem(const std::string &fmt = "%H:%M:%S") : _time_fmt(fmt) {}
    void format(std::ostream &out, const LogMsg &msg) override
    {
      struct tm t; // t是一个结构体，保存时间的各种信息
      localtime_r(&msg._ctime, &t);
      char tmp[32] = {0};
      strftime(tmp, 31, _time_fmt.c_str(), &t); // 将各种信息按照格式组合起来，放到tmp中
      out << tmp;
    }

  private:
    std::string _time_fmt; //%H:%M:%S
  };
  class LineFormatItem : public FormatItem
  {
  public:
    void format(std::ostream &out, const LogMsg &msg) override
    {
      out << msg._line;
    }
  };
  class ThreadFormatItem : public FormatItem
  {
  public:
    void format(std::ostream &out, const LogMsg &msg) override
    {
      out << msg._tid;
    }
  };
  class LoggerFormatItem : public FormatItem
  {
  public:
    void format(std::ostream &out, const LogMsg &msg) override
    {
      out << msg._logger;
    }
  };
  class FileFormatItem : public FormatItem
  {
  public:
    void format(std::ostream &out, const LogMsg &msg) override
    {
      out << msg._file;
    }
  };
  class TabFormatItem : public FormatItem
  {
  public:
    void format(std::ostream &out, const LogMsg &msg) override
    {
      out << "\t";
    }
  };
  class NlineFormatItem : public FormatItem
  {
  public:
    void format(std::ostream &out, const LogMsg &msg) override
    {
      out << "\n";
    }
  };
  class OtherFormatItem : public FormatItem
  {
  public:
    OtherFormatItem(const std::string &str) : _str(str) {}
    void format(std::ostream &out, const LogMsg &msg) override
    {
      out << _str;
    }

  private:
    std::string _str;
  };
  /*
      %d表示日期，包含子格式{%H:%M:%S}
      %t表示线程id
      %c表示日志器名称
      %f表示源文件名
      %l表示源码行号
      %p表示日志级别
      %T表示缩进
      %m表示主体消息
      %n表示换行
  */
  class Formater
  {
  public:
    using ptr = std::shared_ptr<Formater>;
    Formater(const std::string &pattern = "[%d{%H:%M:%S}][%t][%c][%f:%l][%p]%T%m%n") : _pattern(pattern)
    {
      assert(parsePattern());
    }
    // 对msg进行格式化,将格式化规则解析之后，将格式处理子字符串数组填满，然后遍历，根据每个子类和规则处理子字符，放在out
    // 最后返回给stirngstream,返回最后的字符串格式结果
    void format(std::ostream &out, const LogMsg &msg)
    {
      for (auto &item : _items) // 遍历得到的格式化指针数组
      {
        item->format(out, msg); // 对象指针指向函数format函数操作，然后格式化子项，out中
      }
    }
    std::string format(const LogMsg &msg)
    {
      std::stringstream ss;
      format(ss, msg);
      return ss.str();
    }

  private:
    // 对格式化规则进行解析,创建不同的格式化对象同时将格式化子模块的数组填满，以便于后面遍历并对字符串进行格式化
    bool parsePattern()
    {
      // 1.对格式化规则字符串进行解析

      std::vector<std::pair<std::string, std::string>> fmt_order;
      size_t pos = 0;
      std::string key, val;
      while (pos < _pattern.size())
      {
        // 1.处理原始字符串，判断是否等于%

        if (_pattern[pos] != '%')
        {
          val.push_back(_pattern[pos]);
          pos++;
          continue;
        }
        // 1.如果是双%号，
        if (pos + 1 < _pattern.size() && _pattern[pos + 1] == '%')
        {
          val += "%%";
          pos += 2;
          continue;
        }
        // 先处理原始字符串
        if (val.empty() == false)
        {
          fmt_order.push_back(std::make_pair("", val));
          val.clear();
        }

        // 接下来再碰到%,然后处理格式化字符
        pos += 1;
        if (pos == _pattern.size())
        {
          std::cout << "%之后没有对应的格式化字符！\n";
          return false;
        }
        key = _pattern[pos];
        // 然后判断后面是否有子格式，就是出现{}
        pos += 1;
        if (pos < _pattern.size() && _pattern[pos] == '{')
        {
          pos += 1;
          while (pos < _pattern.size() && _pattern[pos] != '}')
          {
            val.push_back(_pattern[pos++]);
          }
          // 走到了末尾跳出了循环，则代表没有遇到}
          if (pos == _pattern.size())
          {
            std::cout << "子规则匹配失败！\n";
            return false;
          }
          pos += 1; // 匹配成功，将上一次的结果加入数组
        }
        fmt_order.push_back(std::make_pair(key, val));
        key.clear();
        val.clear();
        // 则处理下一个字符，循环处理
      }
      // 2.根据解析得到的数据初始化格式化子项数组成员
      for (auto &it : fmt_order)
      {
        _items.push_back(createItem(it.first, it.second));
      }
      return true;
    }

    // 根据不同的格式化字符创建不同的格式化对象
    FormatItem::ptr createItem(const std::string &key, const std::string &val)
    {
      if (key == "d")
        return std::make_shared<TimeFormatItem>(val);
      if (key == "t")
        return std::make_shared<ThreadFormatItem>();
      if (key == "c")
        return std::make_shared<LoggerFormatItem>();
      if (key == "f")
        return std::make_shared<FileFormatItem>();
      if (key == "l")
        return std::make_shared<LineFormatItem>();
      if (key == "p")
        return std::make_shared<LevFormatItem>();
      if (key == "T")
        return std::make_shared<TabFormatItem>();
      if (key == "m")
        return std::make_shared<MsgFormatItem>();
      if (key == "n")
        return std::make_shared<NlineFormatItem>();
      if (key == "")
        return std::make_shared<OtherFormatItem>(val);
      std::cout << "没有对应的格式化字符:%" << key << std::endl;
      abort();
      return FormatItem::ptr();
    }

  private:
    std::string _pattern; // 格式化规则字符串
    std::vector<FormatItem::ptr> _items;
  };

}

#endif